#include "fc_types.h"
#include "fc_mixer.h"

const udword AMIGA_CLOCK_PAL = 3546895;
//const udword AMIGA_CLOCK_NTSC = 3579546;

uword MIXER_patterns;
uword MIXER_samples;
uword MIXER_voices;
ubyte MIXER_speed;
ubyte MIXER_bpm;
ubyte MIXER_count;
uword MIXER_minPeriod;
uword MIXER_maxPeriod;

struct channel logChannel[32];

void (*mixerPlayRout)();
const char* mixerFormatName = 0;

extern void* mixerFill8bitMono( void*, udword );
void* (*mixerFillRout)( void*, udword ) = &mixerFill8bitMono;

static ubyte zero8bit;   // ``zero''-sample

udword pcmFreq;

udword samplesAdd;
udword samplesPnt;
uword samples, samplesOrg;

udword toFill = 0;

ubyte emptySample;


void channel::takeNextBuf()
{
    if (!isOn)
    {
        // If channel is off, take sample START parameters.
        start = paula.start;
        length = paula.length;
        length <<= 1;
        if (length == 0)  // Paula would play $FFFF words (!)
            length = 1;
        end = start+length;
    }
    
    repeatStart = paula.start;
    repeatLength = paula.length;
    repeatLength <<= 1;
    if (repeatLength == 0)  // Paula would play $FFFF words (!)
        repeatLength = 1;
    repeatEnd = repeatStart+repeatLength;
}

void channel::on()
{
    takeNextBuf();

    isOn = true;
}

void channel::updatePerVol()
{
    if (paula.period != curPeriod)
    {
        period = paula.period;  // !!!
        curPeriod = paula.period;
        if (curPeriod != 0)
        {
            stepSpeed = (AMIGA_CLOCK_PAL/pcmFreq) / curPeriod;
            stepSpeedPnt = (((AMIGA_CLOCK_PAL/pcmFreq)%curPeriod)*65536) / curPeriod;
        }
        else
        {
            stepSpeed = stepSpeedPnt = 0;
        }
    }
    
    volume = paula.volume;
    if (volume > 64)
    {
        volume = 64;
    }
}

void mixerInit(ubyte zero8)
{
    zero8bit = zero8;
    
    for ( int i = 0; i < 32; i++ )
    {
        logChannel[i].start = &emptySample;
        logChannel[i].end = &emptySample+1;
        logChannel[i].repeatStart = &emptySample;
        logChannel[i].repeatEnd = &emptySample+1;
        logChannel[i].length = 1;
        logChannel[i].curPeriod = 0;
        logChannel[i].stepSpeed = 0;
        logChannel[i].stepSpeedPnt = 0;
        logChannel[i].stepSpeedAddPnt = 0;
    }
}

void mixerFillBuffer( void* buffer, udword bufferLen )
{
    while ( bufferLen > 0 )
    {
        if ( toFill > bufferLen )
        {
            buffer = (*mixerFillRout)(buffer, bufferLen);
            toFill -= bufferLen;
            bufferLen = 0;
        }
        else if ( toFill > 0 )
        {
            buffer = (*mixerFillRout)(buffer, toFill);
            bufferLen -= toFill;
            toFill = 0;   
        }
	
        if ( toFill == 0 )
        {
            (*mixerPlayRout)();

            register udword temp = ( samplesAdd += samplesPnt );
            samplesAdd = temp & 0xFFFF;
            toFill = samples + ( temp > 65535 );
	  
            for ( int i = 0; i < MIXER_voices; i++ )
            {
                if ( logChannel[i].period != logChannel[i].curPeriod )
                {
                    logChannel[i].curPeriod = logChannel[i].period;
                    if (logChannel[i].curPeriod != 0)
                    {
                        logChannel[i].stepSpeed = (AMIGA_CLOCK_PAL/pcmFreq) / logChannel[i].period;
                        logChannel[i].stepSpeedPnt = (((AMIGA_CLOCK_PAL/pcmFreq) % logChannel[i].period ) * 65536 ) / logChannel[i].period;
                    }
                    else
                    {
                        logChannel[i].stepSpeed = logChannel[i].stepSpeedPnt = 0;
                    }
                }
            }
        }
        
    } // while bufferLen
}

void mixerSetReplayingSpeed()
{
    samples = ( samplesOrg = pcmFreq / 50 );
    samplesPnt = (( pcmFreq % 50 ) * 65536 ) / 50;  
    samplesAdd = 0;
}

void mixerSetBpm( uword bpm )
{
    uword callsPerSecond = (bpm * 2) / 5;
    samples = ( samplesOrg = pcmFreq / callsPerSecond );
    samplesPnt = (( pcmFreq % callsPerSecond ) * 65536 ) / callsPerSecond;  
    samplesAdd = 0; 
}

void* mixerFill8bitMono( void* buffer, udword numberOfSamples )
{
    ubyte* buffer8bit = (ubyte*)buffer;
    for ( int i = 0; i < MIXER_voices; i++ )
    {
        buffer8bit = (ubyte*)buffer;
      
        for ( udword n = numberOfSamples; n > 0; n-- )
        {
            if ( i == 0 )
            {
                *buffer8bit = zero8bit;
            }
            logChannel[i].stepSpeedAddPnt += logChannel[i].stepSpeedPnt;
            logChannel[i].start += ( logChannel[i].stepSpeed + ( logChannel[i].stepSpeedAddPnt > 65535 ) );
            logChannel[i].stepSpeedAddPnt &= 65535;
            if ( logChannel[i].start < logChannel[i].end )
            {
                *buffer8bit += ( logChannel[i].volume * ( (*(sbyte*)logChannel[i].start) / MIXER_voices )) / 64;
            }
            else
            {
                if ( logChannel[i].looping )
                {
                    logChannel[i].start = logChannel[i].repeatStart;
                    logChannel[i].end = logChannel[i].repeatEnd;
                    if ( logChannel[i].start < logChannel[i].end )
                        *buffer8bit += ( logChannel[i].volume * ( (*(sbyte*)logChannel[i].start) / MIXER_voices )) / 64;
                }
            }
            buffer8bit++;
        }
    }
    return(buffer8bit);
}
